#ifndef XG_DATETIME_CPP
#define XG_DATETIME_CPP
//////////////////////////////////////////////////////////////////
#ifdef XG_LINUX
#include <sys/time.h>
#endif

#include "../DateTime.h"

Calendar::Calendar()
{
	year = BASE_YEAR;
	month = 1;
	day = 1;
}
Calendar::Calendar(int year, int month, int day)
{
	this->month = month;
	this->year = year;
	this->day = day;
}
bool Calendar::init(int year, int month, int day)
{
	this->month = month;
	this->year = year;
	this->day = day;

	return getCheckCode() == 0;
}
bool Calendar::setYear(int year)
{
	int temp = this->year;	

	this->year = year;

	if (getCheckCode() == 0) return true;

	this->year = temp;

	return false;
}
bool Calendar::setMonth(int month)
{
	int temp = this->month;	

	this->month = month;

	if (getCheckCode() == 0) return true;

	this->month = temp;

	return false;
}
bool Calendar::setDate(int day)
{
	int temp = this->day;

	this->day = day;

	if (getCheckCode() == 0) return true;

	this->day = temp;

	return false;
}
int Calendar::getYear() const
{
	return year;
}
int Calendar::getMonth() const
{
	return month;
}
int Calendar::getDate() const
{
	return day;
}
bool Calendar::addYear()
{
	++year;

	if (getCheckCode() == 0) return true;

	--year;

	return false;
}
bool Calendar::subYear()
{
	--year;

	if (getCheckCode() == 0) return true;

	++year;

	return false;
}
bool Calendar::addMonth()
{
	++month;

	if (getCheckCode() == 0) return true;

	--month;

	return false;
}
bool Calendar::subMonth()
{
	--month;

	if (getCheckCode() == 0) return true;

	++month;

	return false;
}
bool Calendar::addDate()
{
	++day;

	if (getCheckCode() == 0) return true;

	--day;

	return false;
}
bool Calendar::subDate()
{
	--day;

	if (getCheckCode() == 0) return true;

	++day;

	return false;
}
bool Calendar::isLeapYear() const
{
	return IsLeapYear(year);
}
string Calendar::toString() const
{
	char str[24] = {0};
	
	if (canUse()) snprintf(str, sizeof(str), "%04d-%02d-%02d", year, month, day);
	
	return str;
}
int Calendar::getWeekday() const
{
	return GetWeekday(*this);
}
int Calendar::getMonthWeekCount() const
{
	return GetMonthWeekCount(year, month);
}
int Calendar::getMonthDayCount() const
{
	return GetMonthDayCount(month, this->isLeapYear());
}
int Calendar::getMonthWeeknum() const
{
	int num = -1;
	int weekday = -1;

	weekday = GetMonthFirstWeekday(year, month);

	if (weekday == -1) return -1;

	weekday += day - 1;
	num = weekday / 7;

	if (weekday % 7) ++num;

	return num;
}
int Calendar::getMonthFirstWeekday() const
{
	return GetMonthFirstWeekday(year, month);
}
int Calendar::getMonthLastWeekday() const
{
	return GetMonthLastWeekday(year, month);
}
string Calendar::getMonthWeekdays() const
{
	int weeknum = -1;
	int weekcnt = -1;
	string res = "1234567";

	weekcnt = getMonthWeekCount();

	if (weekcnt == -1) return stdx::EmptyString();

	weeknum = getMonthWeeknum();

	if (weeknum == -1) return stdx::EmptyString();

	if (weeknum > 1 && weeknum < weekcnt) return res;

	int weekday = GetMonthFirstWeekday(year, month);

	if (weekday == -1) return stdx::EmptyString();

	res.clear();

	if (weeknum == 1)
	{
		for (int i = weekday; i <= 7; i++) res += stdx::str(i);

		return res;
	}

	weekday = GetMonthLastWeekday(year, month);

	if (weekday == -1) return res;

	for (int i = 1; i <= weekday; i++) res += stdx::str(i);

	return res;
}
int Calendar::getCheckCode() const
{
	if (year < MIN_YEAR) return 1;

	if (year > MAX_YEAR) return 2;

	if (month < 1) return 3;

	if (month > 12) return 4;

	if (day < 1) return 5;

	int days = -1;

	days = GetMonthDayCount(month, isLeapYear());

	if (day > days) return 6;

	return 0;
}
bool Calendar::IsLeapYear(int year)
{
	if (year % 4) return false;

	if (year % 400 == 0)
	{
		if (year % 3200 == 0)
		{
			if (year % 153600 == 0) return true;

			return false;
		}

		return true;
	}

	if (0 == year % 100) return false;

	return true;
}
int Calendar::GetDayCount(int a, int b)
{
	if (a == b) return 0;

	int cnt = 0;
	bool swap = false;

	if (a > b)
	{
		swap = true;

		SWAP_INTEGER(a, b);
	}

	while (a < b)
	{
		cnt += GetYearDayCount(a++);
	}

	return swap ? -cnt : cnt;
}
int Calendar::GetDayCount(const Calendar& a, const Calendar& b)
{
	bool swap = false;
	int y1 = a.getYear();
	int m1 = a.getMonth();
	int d1 = a.getDate();
	int y2 = b.getYear();
	int m2 = b.getMonth();
	int d2 = b.getDate();

	if (y1 > y2)
	{
		swap = true;

		SWAP_INTEGER(y1, y2);
		SWAP_INTEGER(m1, m2);
		SWAP_INTEGER(d1, d2);
	}
	else if (y1 == y2)
	{
		d1 = GetYearDay(a);
		d2 = GetYearDay(b);

		return d2 - d1;
	}

	int cnt = 0;

	if (y1 + 1 < y2) cnt = GetDayCount(y1 + 1, y2);

	if (swap)
	{
		cnt += GetYearDay(a) + GetYearDayCount(y1) - GetYearDay(b);

		return -cnt;
	}
	else
	{
		cnt += GetYearDay(b) + GetYearDayCount(y1) - GetYearDay(a);

		return cnt;
	}
}
int Calendar::GetYearDay(const Calendar& calendar)
{
	if (calendar.getCheckCode() == 0)
	{
		int days = calendar.getDate();
		int month = calendar.getMonth();
		bool bLeap = calendar.isLeapYear();
		
		for (int i = 1; i < month; i++)
		{
			days += GetMonthDayCount(i, bLeap);
		}

		return days;
	}

	return -1;
}
int Calendar::GetYearDayCount(int year)
{
	if (year < MIN_YEAR || year > MAX_YEAR) return -1;

	int count = 0;
	bool bLeap = IsLeapYear(year);

	for (int i = 1; i <= 12; i++)
	{
		count += GetMonthDayCount(i, bLeap);
	}

	return count;
}
int Calendar::GetMonthDayCount(int month, bool leapyear)
{
	int count = -1;

	if (month <= 0 || month > 12) return -1;

	if (month == 2)
	{
		if (leapyear)
		{
			count = 29;
		}
		else
		{
			count = 28;
		}
	}
	else if (month <= 7)
	{
		if (month % 2 == 0)
		{
			count = 30;
		}
		else
		{
			count = 31;
		}
	}
	else
	{
		if (month % 2 == 0)
		{
			count = 31;
		}
		else
		{
			count = 30;
		}
	}

	return count;
}
int Calendar::GetYearFirstWeekday(int year)
{
	int days = 0;
	int week = -1;

	if (year < MIN_YEAR || year > MAX_YEAR) return -1;

	if (BASE_YEAR == year) return BASE_WEEKDAY;

	if (year > BASE_YEAR)
	{
		for (int i = BASE_YEAR; i < year; i++)
		{
			if (IsLeapYear(i))
			{
				days += 2;		//366%7
			}
			else 
			{
				days += 1;		//365%7
			}
		}

		days %= 7;
		days += BASE_WEEKDAY;
		week = days % 7;
	}
	else
	{
		for (int i = year; i < BASE_YEAR; i++)
		{
			if (IsLeapYear(i))
			{
				days += 2;		//366%7
			}
			else 
			{
				days +=  1;		//365%7
			}
		}

		days %= 7;
		days = BASE_WEEKDAY - days + 7;
		week = days % 7;
	}

	if (0 == week) week = 7;

	return week;
}
int Calendar::GetMonthFirstWeekday(int year, int month)
{
	int days = 0;
	int week = -1;
	int yearWeekday = -1;
	bool isLeapYear = false;

	if (month <= 0 || month > 12) return -1;

	yearWeekday = GetYearFirstWeekday(year);

	if (yearWeekday < 0) return -1;

	isLeapYear = IsLeapYear(year);

	for (int i = 1; i < month; i++)
	{
		days += GetMonthDayCount(i, isLeapYear);
	}

	days %= 7;
	days += yearWeekday;
	week = days % 7;

	if (week == 0) week = 7;

	return week;
}
int Calendar::GetMonthLastWeekday(int year, int month)
{
	int week = -1;

	if (month <= 0 || month > 12) return -1;

	if (month == 12)
	{
		++year;
		month = 1;
	}
	else
	{
		++month;
	}

	week = GetMonthFirstWeekday(year, month);

	if (week == -1) return -1;

	if (--week == 0) week = 7;

	return week;
}
int Calendar::GetWeekday(const Calendar& calendar)
{
	int week = -1;
	int monthWeekday = -1;
	int days = calendar.day;

	monthWeekday = GetMonthFirstWeekday(calendar.year, calendar.month);

	if (monthWeekday == -1) return -1;

	days %= 7;
	days += monthWeekday - 1;
	week = days % 7;

	if (week == 0) week = 7;

	return week;
}
int Calendar::GetMonthWeekCount(int year, int month)
{
	int count = 0;
	int days = -1;
	int weekday = -1;

	days = GetMonthDayCount(month, IsLeapYear(year));
	weekday = GetMonthFirstWeekday(year, month);

	if (weekday == -1) return -1;

	weekday += days - 1;
	count = weekday / 7;

	if (weekday % 7) ++count;

	return count;
}
int Calendar::GetFirstWeekdayDateFromWeeknum(int year, int month, int weeknum)
{
	int num = -1;
	int date = -1;
	int weekday = -1;

	if (weeknum == 1) return 1;

	if (weeknum < 1 || weeknum > 6) return -1;

	weekday = GetMonthFirstWeekday(year, month);

	if (weekday == -1) return -1;

	num = GetMonthWeekCount(year, month);

	if (num == -1) return -1;

	if (weeknum > num) return -1;

	date = 7 - weekday + 1;

	for (int i = 2; i < weeknum; i++) date += 7;

	return date + 1;
}
int Calendar::GetDateFromWeekday(int year, int month, int weeknum, int weekday)
{
	int days = -1;
	int date = -1;
	int week = -1;

	week = GetMonthFirstWeekday(year, month);

	if (week == -1) return -1;

	date = GetFirstWeekdayDateFromWeeknum(year, month, weeknum);

	if (date == -1) return -1;

	if (weeknum == 1)
	{
		while (week <= 7)
		{
			if (week == weekday) return date; 

			++date;
			++week;
		}

		return -1;
	}

	days = GetMonthDayCount(month, IsLeapYear(year));

	if (days == -1) return -1;

	for (int num = 1; num <= 7 && date <= days; date++, num++)
	{
		if (num == weekday) return date;
	}

	return -1;
}


DateTime::DateTime()
{
	yday = -1;
}
DateTime::DateTime(time_t tm)
{
	update(tm);
}
void  DateTime::clearDate()
{
	day = 1;
	yday = 1;
	year = 0;
	month = 1;
}
bool DateTime::canUse() const
{
	if (hour < 0 || hour >= 24) return false;
	if (min < 0 || min >= 60) return false;
	if (sec < 0 || sec >= 60) return false;

	return yday > 0 && toCalendar().getCheckCode() == 0;
}
DateTime& DateTime::update()
{
	return update(time(NULL));
}
DateTime& DateTime::update(time_t tm)
{
	GetDateTime(this, &tm);

	return *this;
}
time_t DateTime::getTime() const
{
	struct tm st;
	
	st.tm_year = year - 1900; 
	st.tm_mon = month - 1; 
	st.tm_mday = day;
	st.tm_hour = hour; 
	st.tm_min = min;
	st.tm_sec = sec;
		  
	return mktime(&st);
}
string DateTime::toString() const
{
	char str[24] = {0};
	
	if (canUse()) snprintf(str, sizeof(str), "%04d-%02d-%02d %02d:%02d:%02d", year, month, day, hour, min, sec);

	return str;
}
string DateTime::getGmtString() const
{
	char str[64] = {0};
	
	if (canUse())
	{
		struct tm tmp;
		time_t tm = getTime();  
	 
		localtime_r(&tm, &tmp);

		strftime(str, sizeof(str) - 1, "%a, %d %b %Y %H:%M:%S", &tmp);
	}
	
	return str;
}
string DateTime::getTimeString() const
{
	char str[24] = {0};
	
	if (canUse()) snprintf(str, sizeof(str), "%02d:%02d:%02d", hour, min, sec);

	return str;
}
string DateTime::getDateString() const
{
	char str[24] = {0};
	
	if (canUse()) snprintf(str, sizeof(str), "%04d-%02d-%02d", year, month, day);

	return str;
}
Calendar DateTime::toCalendar() const
{
	return Calendar(year, month, day);
}
string DateTime::ToString()
{
	return DateTime().update().toString();
}
string DateTime::GetBizId()
{
	DateTime dt;
	char str[24] = {0};
	static atomic_int idx(0);

	if (dt.update().canUse()) snprintf(str, sizeof(str), "%04d%02d%02d%02d%02d%02d%02d", dt.year, dt.month, dt.day, dt.hour, dt.min, dt.sec, ++idx % 100);

	return str;
}
bool DateTime::SetSystemDateTime(const DateTime& dt)
{
#ifdef XG_LINUX
	struct tm t;
	struct timeval tv;
	struct timezone tz;

	tz.tz_minuteswest = 0;
	tz.tz_dsttime = 0;

	t.tm_year = dt.year - 1900;
	t.tm_mon = dt.month - 1;
	t.tm_mday = dt.mday;
	t.tm_hour = dt.hour;
	t.tm_min = dt.min;
	t.tm_sec = dt.sec;

	tv.tv_sec = mktime(&t);
	tv.tv_usec = 0;

	return settimeofday(&tv, &tz) == 0;
#else
	SYSTEMTIME st;

	::memset(&st, 0, sizeof(st));

	st.wMonth = dt.month;
	st.wYear = dt.year;
	st.wDay = dt.day;
	st.wMinute = dt.min;
	st.wSecond = dt.sec;
	st.wHour = dt.hour;

	return SetLocalTime(&st) ? true : false;
#endif
}
DateTime DateTime::FromString(const string& str)
{
	DateTime dt;

	dt.yday = -1;

	if (str.length() < 8) return dt;

	size_t pos = str.find(' ');

	if (pos == string::npos)
	{
		pos = str.find('T');

		if (pos == string::npos) return dt;
	}

	vector<string> vec;
	string a = str.substr(0, pos);
	string b = str.substr(pos + 1);

	int count = stdx::split(vec, a, "-");

	if (count != 3) count = stdx::split(vec, a, "/");

	if (count != 3) return dt;

	dt.year = atoi(vec[0].c_str());
	dt.month = atoi(vec[1].c_str());
	dt.day = atoi(vec[2].c_str());
	dt.yday = Calendar::GetYearDay(dt.toCalendar());

	vec.clear();

	dt.hour = 0;
	dt.min = 0;
	dt.sec = 0;

	count = stdx::split(vec, b, ":");

	if (count > 0) dt.hour = atoi(vec[0].c_str());
	if (count > 1) dt.min = atoi(vec[1].c_str());
	if (count > 2) dt.sec = atoi(vec[2].c_str());

	return dt;
}
long long DateTime::GetSecondsAfterYearFirstDay(const DateTime& dt)
{
	long long cnt = Calendar::GetYearDay(dt.toCalendar());
	
	cnt *= 24 * 60 * 60;

	return cnt + dt.hour * 60 * 60 + dt.min * 60 + dt.sec;
}
long long DateTime::GetSecondsAfterDayFirstSecond(const DateTime& dt)
{
	return dt.hour * 60 * 60 + dt.min * 60 + dt.sec;
}
//////////////////////////////////////////////////////////////////
#endif

